#include "stdafx.h"


CMove::CMove()
{
}


CMove::~CMove()
{
}

uint CMove::Do( CBoard * board, uint move )
{
    board->PushInfo();
    board->SetColor( OPPONENT( board->GetColor()));

    uint to   = BYTE_FROM_UINT(move, 0);
    uint from = BYTE_FROM_UINT(move, 1);
    uint kill = BYTE_FROM_UINT(move, 2);
    uint flag = BYTE_FROM_UINT(move, 3);
    uint pie = board->Get( from );
    uint oops = 0;

    if ( flag == PAWN_EXT )
        board->SetEnpassant( (from+to)>>1 );
    else 
        board->SetEnpassant( 0 );

    if (flag)
    {
        switch( flag )
        {
            case CASTLE_WK:
            {
                if ( ( board->UnderAtack(E1,BLACK ) ) || 
                     ( board->UnderAtack(F1,BLACK ) ) ||
                     ( board->UnderAtack(G1,BLACK ) ) ) {
                            board->PopInfo();
                            return 0; // the castling impossible due to check
                        }
                else {
                        board->Remove( E1 );
                        board->Add( G1, WHITE|KING );
                        board->Remove( H1 );
                        board->Add( F1, WHITE|ROOK );
                        return 1;
                     }  
            }
            case CASTLE_WQ:
            {
                if ( ( board->UnderAtack(E1,BLACK ) ) || 
                     ( board->UnderAtack(D1,BLACK ) ) ||
                     ( board->UnderAtack(C1,BLACK ) ) ) {
                            board->PopInfo();
                            return 0; // the castling impossible due to check
                        }
                else {
                        board->Remove( E1 );
                        board->Add( C1, WHITE|KING );
                        board->Remove( A1 );
                        board->Add( D1, WHITE|ROOK );
                        return 1;
                     }  
            }
            case CASTLE_BK:
            {
                if ( ( board->UnderAtack(E8,WHITE ) ) || 
                     ( board->UnderAtack(F8,WHITE ) ) ||
                     ( board->UnderAtack(G8,WHITE ) ) ) {
                            board->PopInfo();
                            return 0; // the castling impossible due to check
                        }
                else {
                        board->Remove( E8 );
                        board->Add( G8, BLACK|KING );
                        board->Remove( H8 );
                        board->Add( F8, BLACK|ROOK );
                        return 1;
                     }  
            }
            case CASTLE_BQ:
            {
                if ( ( board->UnderAtack(E8,WHITE ) ) || 
                     ( board->UnderAtack(D8,WHITE ) ) ||
                     ( board->UnderAtack(C8,WHITE ) ) ) {
                            board->PopInfo();
                            return 0; // the castling impossible due to check
                        }
                else {
                        board->Remove( E8 );
                        board->Add( C8, BLACK|KING );
                        board->Remove( A8 );
                        board->Add( D8, BLACK|ROOK );
                        return 1;
                     }  
            }
        }



        if ( flag == ENPASSANT )
        {
            int  step       = IS_WHITE( pie ) ? (-10) : ( 10);
            board->Remove( to-step );
        }
    } 

    board->Remove( from );
    board->Remove( to   ); // kill enemy
    // promotion
    if ( MOVE_IS_PROMOTION(flag) )
        board->Add( to, flag );
    else
        board->Add( to, pie );

    return 1;
};
uint CMove::Undo( CBoard * board, uint move )
{

    uint to   = BYTE_FROM_UINT(move, 0);
    uint from = BYTE_FROM_UINT(move, 1);
    uint kill = BYTE_FROM_UINT(move, 2);
    uint flag = BYTE_FROM_UINT(move, 3);

    if (flag)
    {
        switch( flag )
        {
            case CASTLE_WK:
            {
                board->Remove( G1 );
                board->Add( E1, WHITE|KING );
                board->Remove( F1 );
                board->Add( H1, WHITE|ROOK );
                board->PopInfo();
                return 0;
            }  
            case CASTLE_WQ:
            {
                board->Remove( C1 );
                board->Add( E1, WHITE|KING );
                board->Remove( D1 );
                board->Add( A1, WHITE|ROOK );
                board->PopInfo();
                return 0;
            }
            case CASTLE_BK:
            {
                board->Remove( G8 );
                board->Add( E8, BLACK|KING );
                board->Remove( F8 );
                board->Add( H8, BLACK|ROOK );
                board->PopInfo();
                return 0;
            }  
            case CASTLE_BQ:
            {
                board->Remove( C8 );
                board->Add( E8, BLACK|KING );
                board->Remove( D8 );
                board->Add( A8, BLACK|ROOK );
                board->PopInfo();
                return 0;
            }
        }
    }

    uint pie = board->Get( to );
    board->Remove( to );
    
    // promotion
    if ( MOVE_IS_PROMOTION(flag) )
        board->Add( from, GET_COLOR(flag)|PAWN );
    else
        board->Add( from, pie );

    if ( kill ) 
        board->Add( to, kill ); 
    if (flag)
    {
        if ( flag == ENPASSANT )
        {   // recover opponent pawn
            int  step               = IS_WHITE( pie ) ? (-10) : ( 10);
            uint opponent_color     = OPPONENT(GET_COLOR(pie));
            board->Add( to-step, opponent_color|PAWN );
        }
    }
    board->PopInfo();
    return 0;
};

uint CMove::SavePromo( CBoard * board, CMoveList * list, uint from, uint to )
{
    uint piece  = board->Get( from );
    uint b      = board->Get( to );
    uint mycolor = GET_COLOR(piece);
    list->Add(BYTES2UINT(mycolor|QUEEN , b, from, to), ( QUEEN << 16 ) );
    list->Add(BYTES2UINT(mycolor|ROOK  , b, from, to), ( ROOK  << 16 ) );
    list->Add(BYTES2UINT(mycolor|BISHOP, b, from, to), ( BISHOP<< 16 ) );
    list->Add(BYTES2UINT(mycolor|KNIGHT, b, from, to), ( KNIGHT<< 16 ) );
    return 0;
}

uint CMove::SavePromoQuiescence( CBoard * board, CMoveList * list, uint from, uint to )
{
    // For Quiescence search generate Queen promotions only
    uint piece  = board->Get( from );
    uint b      = board->Get( to );
    uint mycolor = GET_COLOR(piece);
    list->Add(BYTES2UINT(mycolor|QUEEN , b, from, to), ( QUEEN << 16 ) );
    // list->Add(BYTES2UINT(mycolor|ROOK  , b, from, to), 0);
    // list->Add(BYTES2UINT(mycolor|BISHOP, b, from, to), 0);
    // list->Add(BYTES2UINT(mycolor|KNIGHT, b, from, to), 0);
    return 0;
}



uint CMove::GenPawn( CBoard * board, CMoveList * list, uint from )
{
    uint piece   = board->Get( from );
    uint mycolor = GET_COLOR(piece);
    uint b;
    // direction to move
    int  step       = IS_WHITE( piece ) ? (-10) : ( 10);
    uint to     = from + step; 
    // captures 
    for ( uint capt = to-1; capt <= to+1; capt+=2 ) {
        b =  board->Get(capt);
        if  ( ARE_FRIENDS( b , OPPONENT( mycolor ) ) )  {
            if ( PROMO_SQARES[capt] )
                SavePromo( board, list, from, capt );
            else // just kill 
                list->Add(BYTES2UINT(0,b,from,capt ), (GET_PIE(b) << 16 ) - PAWN );
        }
        uint enp = board->GetEnpassant();
        // enpassant
        if  ( (enp == capt) && ( mycolor == PAWN_ENPASSANT[enp] ) ) {
            list->Add(BYTES2UINT(ENPASSANT,b,from,capt ), ( PAWN << 16 ) - PAWN );
        }
        

    }
    // 1 step
    b =  board->Get(to);
    if  ( IS_EMPTY( b ) )  {
        // todo: add promotions
        if ( PROMO_SQARES[to] )
            SavePromo( board, list, from, to );
        else // just add move
            list->Add(BYTES2UINT(0,0,from,to), 0);
        // 2 step
        uint to2 = to+step; 
        uint b2  = board->Get(to2);
        if ( ( ARE_FRIENDS( piece, PAWN_STARTUP[from]) ) && IS_EMPTY( b2 ) )
            list->Add(BYTES2UINT( PAWN_EXT , 0, from, to2), 0);
    }
    return 0;
}

uint CMove::GenPawnQuiescence( CBoard * board, CMoveList * list, uint from )
{
    uint piece   = board->Get( from );
    uint mycolor = GET_COLOR(piece);
    uint b;
    // direction to move
    int  step       = IS_WHITE( piece ) ? (-10) : ( 10);
    uint to     = from + step; 
    // captures 
    for ( uint capt = to-1; capt <= to+1; capt+=2 ) {
        b =  board->Get(capt);
        if  ( ARE_FRIENDS( b , OPPONENT( mycolor ) ) )  {
            if ( PROMO_SQARES[capt] )
                SavePromoQuiescence( board, list, from, capt );
            else // just kill 
                list->Add(BYTES2UINT(0,b,from,capt ), (GET_PIE(b) << 16 ) - PAWN );
        }
        uint enp = board->GetEnpassant();
        // enpassant
        if  ( (enp == capt) && ( mycolor == PAWN_ENPASSANT[enp] ) ) {
            list->Add(BYTES2UINT(ENPASSANT,b,from,capt ), ( PAWN << 16 ) - PAWN );
        }
    }
    return 0;
}

uint CMove::GenSlider( CBoard * board, CMoveList * list, uint from )
{
    uint to, b;
    uint pie = board->Get(from);
    uint p   = GET_PIE(pie);
    // slide pieces
    for (uint dir = DIRFROM[p]; dir < DIRTO[p]; dir++ )
    {
        to = from;
        for ( uint step = 0; step < DIRLEN[p]; step++ )
        {
            to += SLIDE_MOVE[dir];
            b = board->Get(to);
            if (OUT_OF_DESK(b) || ARE_FRIENDS(pie,b)) break;
            else {
                if ( b )
                    list->Add(BYTES2UINT(0,b,from,to), (GET_PIE(b) << 16 ) - p ); // capture move eval
                else
                    list->Add(BYTES2UINT(0,b,from,to), 0); 
                if ( b ) break;
            }
        }
    }
    return 0;
}

uint CMove::GenSliderQuiescence( CBoard * board, CMoveList * list, uint from )
{
    uint to, b;
    uint pie = board->Get(from);
    uint p   = GET_PIE(pie);
    // slide pieces
    for (uint dir = DIRFROM[p]; dir < DIRTO[p]; dir++ )
    {
        to = from;
        for ( uint step = 0; step < DIRLEN[p]; step++ )
        {
            to += SLIDE_MOVE[dir];
            b = board->Get(to);
            if (OUT_OF_DESK(b) || ARE_FRIENDS(pie,b)) break;
            else {
                if ( b ) { // capture
                    list->Add(BYTES2UINT(0,b,from,to), (GET_PIE(b) << 16 ) - p );
                    break;
                }
            }
        }
    }
    return 0;
}

uint CMove::GenAll( CBoard * board, CMoveList * list, uint color )
{
    list->Reset();
    board->Select( color );

    uint from, pie;
    while( from = board->Next() )
    {
        pie = board->Get(from);
        // todo
        uint p = GET_PIE(pie);
        if ( p != PAWN )
            GenSlider( board, list, from );
        else
            GenPawn( board, list, from );
    }
    GenCastles( board, list, color );
    return 0;
}

uint CMove::GenCaptures( CBoard * board, CMoveList * list, uint color )
{
    list->Reset();
    board->Select( color );

    uint from, pie;
    while( from = board->Next() )
    {
        pie = board->Get(from);
        // todo
        uint p = GET_PIE(pie);
        if ( p != PAWN )
            GenSliderQuiescence( board, list, from );
        else
            GenPawnQuiescence( board, list, from );
    }
    return 0;
}

uint CMove::GenAll( CBoard * board, CMoveList * list )
{
    GenAll( board, list, board->GetColor() );
    return 0;
}

uint CMove::GenCaptures( CBoard * board, CMoveList * list )
{
    GenCaptures( board, list, board->GetColor() );
    return 0;
}

uint CMove::GenCastles( CBoard * board, CMoveList * list, uint color )
{
    uint castles = board->GetFlags();
    if ( IS_WHITE(color) && 
        ( castles & CASTLE_WK ) && 
        ( board->Get( E1 ) == WK ) && 
        ( board->Get( H1 ) == WR ) && 
        ( board->Get( F1 ) == 0 ) && 
        ( board->Get( G1 ) == 0 ) )
        list->Add(BYTES2UINT(CASTLE_WK,0,E1,G1), 0); 
    if ( IS_WHITE(color) &&     
        ( castles & CASTLE_WQ ) && 
        ( board->Get( E1 ) == WK ) && 
        ( board->Get( A1 ) == WR ) && 
        ( board->Get( B1 ) == 0 ) && 
        ( board->Get( C1 ) == 0 ) &&
        ( board->Get( D1 ) == 0 ) )
        list->Add(BYTES2UINT(CASTLE_WQ,0,E1,C1), 0); 
    if ( IS_BLACK(color) && 
        ( castles & CASTLE_BK ) && 
        ( board->Get( E8 ) == BK ) && 
        ( board->Get( H8 ) == BR ) && 
        ( board->Get( F8 ) == 0 ) && 
        ( board->Get( G8 ) == 0 ) )
        list->Add(BYTES2UINT(CASTLE_BK,0,E8,G8), 0); 
    if ( IS_BLACK(color) &&     
        ( castles & CASTLE_BQ ) && 
        ( board->Get( E8 ) == BK ) && 
        ( board->Get( A8 ) == BR ) && 
        ( board->Get( B8 ) == 0 ) && 
        ( board->Get( C8 ) == 0 ) &&
        ( board->Get( D8 ) == 0 ) )
        list->Add(BYTES2UINT(CASTLE_BQ,0,E8,C8), 0); 
    return 0;
}

uint CMove::ToString( uint move, char s[256])
{
    uint from = BYTE_FROM_UINT( move, 1 );
    uint to   = BYTE_FROM_UINT( move, 0 );
    uint kill = BYTE_FROM_UINT( move, 2 );
    uint pro  = BYTE_FROM_UINT( move, 3 );
    strcpy_s(s, 256, "");

    strcat_s(s, 256, SQ_NAME64[ MAILBOX[ from ] ]);
    strcat_s(s, 256, SQ_NAME64[ MAILBOX[ to   ] ]);
    if ( MOVE_IS_PROMOTION(pro) )
    {
        strcat_s(s, 256, PIE2CHAR[ GET_PIE(pro) ]);
    }
    return 0;
}